package cz.proks.bank.manager;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Date;
import java.util.List;

import javax.activation.DataHandler;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import cz.proks.bank.BankConstants;
import cz.proks.bank.dao.AccountDao;
import cz.proks.bank.domain.Account;
import cz.proks.bank.domain.Bank;
import cz.proks.bank.domain.OperationLog;
import cz.proks.bank.domain.OperationLog.OperationType;
import cz.proks.bank.domain.Payment;
import cz.proks.bank.manager.exeception.AccountNotFoundException;
import cz.proks.bank.manager.exeception.BankException;
import cz.proks.bank.manager.exeception.NotEnoughAvailableMoneyException;
import cz.proks.bank.manager.exeception.ReportException;
import cz.proks.bank.manager.exeception.TransferException;
import cz.proks.bank.manager.exeception.UnsupportedCurrencyException;

@Service
public class AccountManager {
	private final Logger log = Logger.getLogger(getClass());
	
	@Autowired
	private AccountDao accountDao;

	@Autowired
	private CurrencyManager currencyManager;

	@Autowired
	private PaymentManager paymentManager;	
	
	@Autowired
	private BankManager bankManager;
	
	@Autowired
	private OperationLogManager logManager;
	
	@Autowired
	private ReportManager reportManager;
	
	public Account getAccount(int accountNumber) throws AccountNotFoundException {
		Account account = accountDao.get(accountNumber);

		if (account == null)
			throw new AccountNotFoundException("Account " + accountNumber
					+ " not found!");

		return account;
	}

	public Account createAccount(String bankCode, String name, String currency) {
		Bank bank = bankManager.getBankByCode(bankCode);
		
		Account account = new Account();

		account.setBank(bank);
		account.setName(name);
		account.setCurrency(currency);

		return createAccount(account);
	}
	
	public Account createAccount(Account account) {
		if (account == null)
			return null;
			
		account = accountDao.create(account);

		logManager.createLog(account, account.getBank(), OperationType.NEW_ACCOUNT, new Date(), "Created account: " + account.toString());
		return account;
	}
	
	public Account updateAccount(Account account) {
		if (account == null)
			return null;
		
		account = accountDao.update(account);
		
		return account;
	}
	
	public long depositMoney(int accountNumber, String currency, int amount) throws AccountNotFoundException, UnsupportedCurrencyException {		
		Account account = getAccount(accountNumber);

		long amountToSave = currencyManager.convert(amount, currency, account.getCurrency());

		account.setAvailableMoney(account.getAvailableMoney() + amountToSave);

		updateAccount(account);
		
		logManager.createLog(account, account.getBank(), OperationType.MONEY_DEPOSIT, new Date(), "Deposited " + amount + " " + currency + " (" + amountToSave + " " + account.getCurrency() + ")");
		return account.getAvailableMoney();
	}

	public long withdrawMoney(int accountNumber, String currency, int amount)
			throws AccountNotFoundException, UnsupportedCurrencyException,
			NotEnoughAvailableMoneyException {
		Account account = getAccount(accountNumber);

		long amountToWithdraw = currencyManager.convert(amount, currency, account.getCurrency());

		if (account.getAvailableMoney() - amountToWithdraw < 0)
			throw new NotEnoughAvailableMoneyException("Cannot withdraw "
					+ amount
					+ " "
					+ currency
					+ " "
					+ (!account.getCurrency().equals(currency) ? "("
							+ amountToWithdraw + " " + account.getCurrency()
							+ ") " : "") + "from account " + accountNumber
					+ ", available money are " + account.getAvailableMoney()
					+ " " + account.getCurrency());

		account.setAvailableMoney(account.getAvailableMoney()
				- amountToWithdraw);

		account = accountDao.update(account);

		logManager.createLog(account, account.getBank(), OperationType.MONEY_WITHDRAW, new Date(), "Withdrawed " + amount + " " + currency + " (" + amountToWithdraw + " " + account.getCurrency() + ")");
		return account.getAvailableMoney();
	}

	public long getAvalilableMoney(int accountNumber, String currency)
			throws AccountNotFoundException, UnsupportedCurrencyException {
		Account account = getAccount(accountNumber);

		long availableMoney = currencyManager.convert(
				account.getAvailableMoney(), account.getCurrency(), currency);

		return availableMoney;
	}

	public void transferMoney(int sourceAccountNumber, int targetAccountNumber, String targetBankCode, long amount, String currency) throws TransferException {
		try {
			Payment payment = paymentManager.buildPayment(sourceAccountNumber, targetAccountNumber, BankConstants.BANK_CODE, targetBankCode, amount, currency);
			paymentManager.processPayment(payment); 
		}
		catch (BankException e) {
			throw new TransferException(e);
		}
	}	
	
	public DataHandler getAccountReport(int accountNumber) throws ReportException {
		Account account = accountDao.get(accountNumber);
		
		List<OperationLog> logs = logManager.listByAccount(account);
		
		DataHandler dataHandler = reportManager.generateReport(account, logs);
		
		saveTempReport(dataHandler);
		
		return dataHandler;		
	}
	
	public boolean isLocalAccount(int accountNumber) {
		return accountDao.get(accountNumber) != null;
	}
	
	public boolean isLocalBankAccount(int accountNumber, String bankCode) {
		return bankManager.isLocalBank(bankCode) && isLocalAccount(accountNumber);
	}
	
	private void saveTempReport(DataHandler dataHandler) {
		if (dataHandler == null)
			return;
		
		File file = new File("D:\\Plocha\\tempReport.pdf");
		
		try {
			if (file.exists())
				file.createNewFile();
		
			OutputStream out = new FileOutputStream(file, false);
		
			dataHandler.writeTo(out);
			
			out.close();
			
		} catch (IOException e) {
			log.error("", e);
		}
	}
}
